package com.michael.demos.base.java.reference;

import java.lang.ref.*;

/**
 * 类功能描述:
 * <pre>
 *   参考：https://juejin.cn/post/6844903665241686029
 *   JAVA 中的4种对象引用级别
 * </pre>
 *
 * @author Michael
 * @version 1.0
 * @date 2020/12/25 8:51
 */
public class ReferenceDemo {

    @SuppressWarnings({"UnusedAssignment", "unused"})
    public static void main(String[] args) {

        //
        /**
         * 强引用：内存不足时不会回收，JVM宁愿抛出OOM错误
         * 1.1. 方法内部的强引用保存在JAVA栈中，真正引用的内容保存在JAVA堆中。
         * 1.2. 当方法运行完成后，会退出方法栈，引用对象的引用数为0，对象会被回收。
         * 2.1. 如果是全局变量，需要回收时需要对对象赋值为null，才能被回收
         * 2.2.
         */
        Object strongReference = new Object();
        strongReference = null;


        /**
         * 软引用：内存空间不足时会回收，通常可用做内存敏感的高速缓存
         * 1.1. 软引用可以与 ReferenceQueue 联合使用
         * 1.2. 如果软引用所引用对象被垃圾回收，JAVA虚拟机就会把这个软引用加入到与之关联的引用队列中。
         * 2.1. 当内存不足时，JVM首先将软引用中的对象引用置为null，然后通知垃圾回收器进行回收
         * 2.2. JVM 会尽可能优先回收长时间闲置不用的软引用对象
         */
        ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
        String str = "abc";
        SoftReference<String> softReference = new SoftReference<>(str, referenceQueue);
        str = null;
        // Notify GC
        System.gc();
        // 正常打印 abc
        System.out.println(softReference.get());

        Reference<? extends String> reference = referenceQueue.poll();
        // 打印 null
        System.out.println(reference);


        /**
         * 弱引用，内存空间足够也可能被回收，当被垃圾回收线程扫描到时就会被回收，垃圾回收线程优先级较低。
         * 同软引用，也能联合 引用队列一起使用，效果一致
         * 弱引用可以在特定场景中做为缓存使用
         */
        str = "abc";
        WeakReference<String> weakReference = new WeakReference<>(str);
        // 弱引用转强引用
        String strongReference2 = weakReference.get();


        /**
         * 虚引用：虚引用并不会决定对象的生命周期。
         * 如果一个对象仅持有虚引用，那么它就和没有任何引用一样，在任何时候都可能被垃圾回收器回收。
         * 虚引用必须和引用队列(ReferenceQueue)联合使用
         * 虚引用主要用来跟踪对象被垃圾回收器回收的活动。
         *
         * 程序可以通过判断引用队列中是否已经加入了虚引用，来了解被引用的对象是否将要进行垃圾回收。如果程序发现某个虚引用已经被加入到引用队列，那么就可以在所引用的对象的内存被回收之前采取必要的行动。
         */
        str = "abc";
        ReferenceQueue queue = new ReferenceQueue();
        // 创建虚引用，要求必须与一个引用队列关联
        PhantomReference phantomReference = new PhantomReference(str, queue);
    }
}
